#define _J_DRETVE_C_
#include "dretve.h"

#include <dretve.h>

#include "red.h"
#include "vrijeme.h"
#include "pozivi_jezgre.h"
#include "razno.h"
#include "liste.h"
#include "postavke.h"
#include "ispis.h"
#include <arh/kontekst.h>

/******************************************************************************/
/*	>>>>>>>>	Podaci jezgre (vecina)		>>>>>>>>	      */
/******************************************************************************/

//stogovi za dretve
int j_stog_dretve[NAJ_DRETVI][VELICINA_STOGA / sizeof(int)];

//stog za jezgrine funkcije (kada se zovu iz prekida)
int j_stogjf[VELICINA_STOGA / sizeof(int)];
int *j_prekidni_stog = POCETAK_STOGA(j_stogjf, VELICINA_STOGA/sizeof(int));


//OPISNICI dretvi
ZAUZMI(j_opisnikdretve_t, j_dretva, NAJ_DRETVI);

//pokazivac na opisnik AKTIVNE dretve
j_opisnikdretve_t *j_aktivna;
void **j_stog_aktivne;	//promjenom aktivne dretve treba azurirat i ovo

//lista PRIPRAVNIH dretvi (struktura)
j_lista_dretvi_t j_pripravne_dretve[BRRAZPRI];
uint32 mj_pripravne_dretve[VELMASKE(BRRAZPRI)] = {0};

/******************************************************************************/
/*	<<<<<<<<	Podaci jezgre (vecina)		<<<<<<<<	      */
/******************************************************************************/

//interne funkcije samo za jezgra/dretve.c
static void j_pokreni_dretvu (void (*pocetna_funkcija)(void *), void *param);
static void j_stavi_u_pripravne (j_opisnikdretve_t *opd);
static void j_latentna_dretva (void *x);

void j_visedretvenost_inicijaliziraj()
{
	int i;

	//pocetna dretva
	j_dretva[0].id = 0;
	j_dretva[0].prio = 0;
	j_dretva[0].rasp = RASP_PRP;
	j_dretva[0].iduci = NULL;
	j_dretva[0].red = AKTIVNA;
	j_dretva[0].alarm = -1;
	j_dretva[0].kraj = -1;
	j_dretva[0].povvr = NULL;
	OZNACI_ZAUZETO(j_dretva, 0);

	j_aktivna = &j_dretva[0];
	j_stog_aktivne = &j_aktivna->stog;

	for (i = 0; i < BRRAZPRI; i++) {
		j_pripravne_dretve[i].prvi = NULL;
		j_pripravne_dretve[i].zadnji = NULL;
	}

	for (i = 1; i < NAJ_DRETVI; i++) {
		j_dretva[i].red = -1;
	}

	j_redovi_inicijaliziraj();
	
	//stvori latentnu dretvu (idle dretvu)
	struct {
		void (*poc_funkc)(void *);	//pocetna funckija dreve
		void *param;			//parametar za stog
		void *stog;			//pocetni stog
		int prio;			//prioritet
		int rasp;			//koji rasporedjivac? FIFO, RR
	} pd;

	pd.poc_funkc = j_latentna_dretva;
	pd.param = NULL;
	pd.stog = NULL;
	pd.prio = PRIO_MIN;
	pd.rasp = RASP_PRP;

	j_stvori_dretvu (&pd);
}

//pomocne funkcije
void j_samo_stavi_u_pripravne (j_opisnikdretve_t *opd)
{
	DODAJ_NA_KRAJ(&j_pripravne_dretve[opd->prio], opd);
	opd->red = PRIPRAVNA;
	OZNACI_RED(j_pripravne_dretve, opd->prio);
}

static void j_stavi_u_pripravne (j_opisnikdretve_t *opd)
{
	j_samo_stavi_u_pripravne(opd);

	j_aktiviraj_prvu_pripravnu();
}

void j_aktiviraj_prvu_pripravnu ()
{
	int prio = PRVI_RED(j_pripravne_dretve);

	//aktivna se mijenja?
	if (j_aktivna == NULL || j_aktivna->red != AKTIVNA || 
			(prio != -1 && j_aktivna->prio > prio))
	{
		if (prio == -1 || j_pripravne_dretve[prio].prvi == NULL) {
			//j_ispisi("prio=%d\n", prio);
			j_zaustavi_potpuno("Red pripravnih prazan!\n");
		}
		
		//stavi trenutno aktivnu u pripravne (jer ce sad druga bit akt.)
		if (j_aktivna != NULL && j_aktivna->red == AKTIVNA)
			j_samo_stavi_u_pripravne(j_aktivna);

		j_aktivna = j_pripravne_dretve[prio].prvi;

		j_pripravne_dretve[prio].prvi = j_aktivna->iduci;

		if (j_pripravne_dretve[prio].prvi == NULL)
			OBRISI_RED(j_pripravne_dretve, prio);

		j_aktivna->iduci = NULL;
		j_azuriraj_stog();
		j_aktivna->red = AKTIVNA;
	}
}

//pocetna funkcija za sve dretve, iz argumenata se uzima funkcija koju je zadao
//korisnik te se ona poziva;
//kada dodje do kraja, dretva zavrsava - NIJE jezgrina funkcija
static void j_pokreni_dretvu (void (*pocetna_funkcija)(void *), void *param)
{
	pocetna_funkcija(param);
	
	//oznaci zavrsetak dretve
	j_funkcija(DRETVA_ZAVRSETAK, NULL);
}


//stvaranje nove dretve - poziva se iz prekida
int j_stvori_dretvu (void *param)
{
	struct {
		void (*poc_funkc)(void *);	//pocetna funckija dreve
		void *param;			//parametar za stog
		void *stog;			//pocetni stog
		int prio;			//prioritet
		int rasp;			//koji rasporedjivac? FIFO, RR
	} *pd = param;

	int i, povr_vr = 0;
	j_opisnikdretve_t *opd;

	PARAMNULL(pd);

	if (pd->poc_funkc == NULL) {
		j_ispisi("poc_funkc je NULL!");
		povr_vr = -1;
	}
	else {
		i = DOHVATI(j_dretva);

		if (i != -1) {
			opd = &j_dretva[i];
			opd->id = i;
			opd->stog = pd->stog;
			opd->iduci = NULL;
			opd->prio = pd->prio;
			opd->rasp = pd->rasp;
			opd->red = -1;
			opd->alarm = -1;
			opd->kraj = -1;
			opd->povvr = NULL;

			//postavi kazaljku stoga, a stog sredi tako da se
			//povratkom iz prekida vraca/pocinje s ovom dretvom

			if (opd->stog == NULL) //postavi stog
				opd->stog = POCETAK_STOGA(j_stog_dretve[i],
							VELICINA_STOGA);
			
			//stavi parametre na stog za funkciju: j_pokreni_dretvu
			STAVI_NA_STOG(opd->stog, pd->param); //2. parametar fun.
			STAVI_NA_STOG(opd->stog, pd->poc_funkc); //1.parametar

			STAVI_NA_STOG(opd->stog, 0);
			//U normalnom pozivu potprograma nakon parametara,
			//instrukcija call na stog stavlja povratnu adresu.
			//Za dretvu je ovo pocetna funkcija iz koje se ne vraca
			//pa vrijednost 0 je odgovarajuca
			
			opd->stog = arh_izgradi_prekidni_stog(opd->stog,
							j_pokreni_dretvu);
			
			//dodaj dretvu u pripravne
			j_stavi_u_pripravne(opd);

			//povratna vrijednost jezgrine funkcije - id dretve
			povr_vr = i;
		}
		else {
			j_ispisi("Nema slobodna opisnika za dretvu");
			povr_vr = -1;
		}
	}
	
	return povr_vr;
}


//'jezgrina funkcija', poziva se iz prekida
//daj procesor iducoj dretvi iz reda pripravnih
int j_dretva_odstupi (void *param)
{
	j_opisnikdretve_t *akt = j_aktivna;

	j_stavi_u_pripravne(j_aktivna);

	if (akt != j_aktivna)
		return 0; //dogodila se promjena dretve
	else
		return 1; //nije se dogodila promjena dretve
}

//kraj rada aktivne dretve, makni je iz reda aktivne
int j_dretva_zavrsetak (void *param)
{
	j_opisnikdretve_t *akt = j_aktivna;
	
	//j_obrisi_dretvu(j_aktivna);
	akt->red = PASIVNA;

	//ima dretvi koje cekaju na kraj ove?
	if (akt->kraj != -1) {
		j_obrisi_red(akt->kraj); //pri brisanju reda se odblokiraju sve
					  //dretve koje cekaju u redu
		akt->kraj = -1;
	}

	OBRISI(j_dretva, akt->id);

	j_aktiviraj_prvu_pripravnu();

	return 0;
}

//vraca id trenutne dretve (moze se pozvati i bez prekida za sada)
int j_id_dretve (void *param)
{
	return j_aktivna->id;
}

//prioriteti
int j_dohvati_prioritet (void *param)
{
	int *pid = param, id, prio = -1;
	
	if (pid == NULL || (id = *pid) == 0)
		id = j_aktivna->id;
		
	if (id >= 0 && id < NAJ_DRETVI && !PROVJERI_SLOBODNO(j_dretva, id) )
		prio = j_dretva[id].prio;

	return prio;
}

//promijeni prioritet aktivnoj dretvi (ona poziva ovu funkciju)
int j_postavi_prioritet (void *param)
{
	int *pprio = param, prio, sprio = j_aktivna->prio;
	
	PARAMNULL(pprio);

	if (j_aktivna->id == 0) {
		j_ispisi("Pocetna dretva ne moze mijenjati prioritet!\n");
		return -1;
	}
	
	prio = *pprio;

	if (prio < PRIO_MAX || prio > PRIO_MIN) {
		j_ispisi("Prioritet izvan dozvoljenih granica!\n");
		return -1;
	}
		
	j_aktivna->prio = prio;
	
	if (prio > sprio) //smanjio prioritet, mozda je sad neka druga prije
		j_aktiviraj_prvu_pripravnu();

	return sprio;
}

//interna funkcija - ne provjeravam parametre
//promijeni prioritet dretvi 'id'
int j_povecaj_prioritet (int id_dretve, int nprio, void *razlog)
{
	j_opisnikdretve_t *opd = &j_dretva[id_dretve];
	
	if (j_povecaj_din_prio(&opd->dp, opd->prio, nprio, razlog))
		return 1;

	return j_promijeni_prioritet(id_dretve, nprio);
}

int j_vrati_prioritet (int id_dretve, void *razlog)
{
	j_opisnikdretve_t *opd = &j_dretva[id_dretve];
	int nprio;
	
	nprio = j_vrati_din_prio(&opd->dp, razlog);
	
	if (nprio == -1)
		return 1;

	return j_promijeni_prioritet(id_dretve, nprio);
}

int j_promijeni_prioritet (int id_dretve, int nprio)
{
	j_opisnikdretve_t *opd = &j_dretva[id_dretve];
	
	if (opd->red == AKTIVNA) {
		opd->prio = nprio;
	}
	else if (opd->red == PRIPRAVNA) {
		//makni iz trenutne razine
		MAKNI_IZ_LISTE(	&j_pripravne_dretve[opd->prio], opd);
		if (j_pripravne_dretve[opd->prio].prvi == NULL)
			OBRISI_RED(j_pripravne_dretve, opd->prio);
	
		//promijeni prioritet
		opd->prio = nprio;
		
		//dodaj u novu razinu
		DODAJ_NA_KRAJ(&j_pripravne_dretve[nprio], opd);
		OZNACI_RED(j_pripravne_dretve, nprio);
	}
	else if (opd->red != PASIVNA) { //negdje ceka
		j_makni_iz_reda(opd->red, opd);

		opd->prio = nprio;

		j_stavi_u_red(opd->red, opd, NULL);
	}
	
	j_aktiviraj_prvu_pripravnu(); //azuriraj aktivnu - mozda se promijenila

	return 0;
}

int j_cekaj_kraj_dretve (void *param)
{
	int *pid = param, id;
	
	PARAMNULL(pid);
	id = *pid;
	
	if (id > 0 && id < NAJ_DRETVI) {
		if (j_dretva[id].red == PASIVNA)
			return 1; //dretva je vec prije zavrsila
		
		if (j_dretva[id].id >= 0) {
			j_dretva[id].kraj = j_dohvati_red(REDDRETVE,SORT_PRP,0);
			if (j_dretva[id].kraj == -1)
				return -1;
			
			j_stavi_u_red(j_dretva[id].kraj, j_aktivna, NULL);

			j_aktiviraj_prvu_pripravnu();
			
			return 0;
		}
	}

	return -1;
}

//aktivira se kad nema nista drugo
static void j_latentna_dretva (void *x)
{
	while(1)
		j_zaustavi();
}

inline j_opisnikdretve_t *j_aktivna_dretva()
{
	return j_aktivna;
}

inline j_opisnikdretve_t *j_dohvati_opisnik (int id)
{
	if (id == -1 || id >= NAJ_DRETVI || PROVJERI_SLOBODNO(j_dretva, id))
		return NULL;
	else
		return &j_dretva[id];
}

inline void j_azuriraj_stog ()
{
	j_stog_aktivne = &j_aktivna->stog;	
}

//za ograniceno blokiranje dretve
void j_postavi_dretvin_alarm (j_opisnikdretve_t *opd, void *_koliko)
{
	vrijeme_t *koliko = _koliko;
	alarm_t t;

	if (opd == NULL || PROVJERI_SLOBODNO(j_dretva, opd->id) || 
		koliko == NULL || (koliko->sekunde | koliko->nsekunde) == 0)
		return;

	t.id = 0;
	t.kada = *koliko;
	t.obrada = NULL;
	t.param = opd;
	t.zast = STVORINOVI | OSLOBODIDR;

	opd->alarm = j_alarm_dohvati(&t);
}
void j_obrisi_dretvin_alarm (j_opisnikdretve_t *opd)
{
	if (opd == NULL || PROVJERI_SLOBODNO(j_dretva, opd->id) || 
							opd->alarm == -1)
		return;

	j_alarm_obrisi(&opd->alarm);
	opd->alarm = -1;
}

//"nasilno" odblokiranje dretve iz reda (npr. istekao je alarm za cekanje)
void j_odblokiraj_dretvu (j_opisnikdretve_t *opd)
{
	if (opd == NULL || PROVJERI_SLOBODNO(j_dretva, opd->id) ||
		opd->red == AKTIVNA || opd->red == PRIPRAVNA)
		return;
	
	j_makni_iz_reda (opd->red, opd);
	j_stavi_u_pripravne (opd);

	if (opd->alarm != -1) {
		j_alarm_obrisi(&opd->alarm);
		opd->alarm = -1;
	}
	if (opd->povvr != NULL) {
		*opd->povvr = -2;
		opd->povvr = NULL;
	}
}

void j_zapisi_adresu_povratne_vrijednosti(int *ppovvr)
{
	j_aktivna->povvr = ppovvr;
}